package com.jy.modules.cims.interact;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.jy.modules.cims.common.CimsConstant;
import com.jy.modules.cims.common.exception.CimsException;
import com.jy.modules.cims.common.exception.CimsExceptionConstant;
import com.jy.modules.cims.common.sysUtils.CollectionsUtils;
import com.jy.modules.cims.common.sysUtils.DateTimeUtils;
import com.jy.modules.cims.common.sysUtils.ReflectUtils;
import com.jy.modules.cims.common.sysUtils.SimpleStringUtils;
import com.jy.modules.cims.common.sysUtils.SpringContextUtils;
import com.jy.modules.cims.data.dao.InteractClassInvokeDao;
import com.jy.modules.cims.data.dao.InteractClassParamterDao;
import com.jy.modules.cims.data.dto.InteractClassInvokeDto;
import com.jy.modules.cims.data.dto.InteractClassParamterDto;

/**
 * @classname InteractClassInvokeService
 * @description 执行接口信息服务实现类
 * @author shengzhoutao
 */
@Service("com.jy.modules.cims.interact.InteractClassInvokeService")
public class InteractClassInvokeService {
	private static final Logger logger = LoggerFactory.getLogger(InteractClassInvokeService.class);
	
	@Autowired
	private InteractClassInvokeDao interactClassInvokeDao;

	@Autowired
	private InteractClassParamterDao interactClassParamterDao;

	@Autowired
	private SpringContextUtils springContextUtils;

	/**
	 * @author shengzhoutao
	 * @description: 分页查询 T_C_MM_INTERACT_CLASS_INVOKE列表
	 * @date 2015-02-02 17:47:37
	 * @param searchParams
	 *            条件
	 * @return
	 * @throws
	 */
	public List<InteractClassInvokeDto> searchInteractClassInvokeByPaging(Map<String, Object> searchParams) throws Exception {
		List<InteractClassInvokeDto> dataList = interactClassInvokeDao.searchInteractClassInvokeByPaging(searchParams);
		return dataList;
	}

	/**
	 * @author shengzhoutao
	 * @description: 按条件查询T_C_MM_INTERACT_CLASS_INVOKE列表
	 * @date 2015-02-02 17:47:37
	 * @param searchParams
	 *            条件
	 * @return
	 * @throws
	 */
	public List<InteractClassInvokeDto> searchInteractClassInvoke(Map<String, Object> searchParams) throws Exception {
		List<InteractClassInvokeDto> dataList = interactClassInvokeDao.searchInteractClassInvoke(searchParams);
		return dataList;
	}

	/**
	 * @Description 根据主键查询执行接口信息，以及执行接口对应的参数信息
	 * @param id
	 *            主键id
	 * @return
	 * @throws Exception
	 *             InteractClassInvokeDto
	 * @author shengzhoutao
	 * @date 2015-2-3-上午10:42:50
	 * @update
	 * @throws
	 */
	public InteractClassInvokeDto queryInteractClassInvokeByPrimaryKey(final String id) throws Exception {

		InteractClassInvokeDto dto = interactClassInvokeDao.findInteractClassInvokeByPrimaryKey(id);

		if (dto == null) {
			dto = new InteractClassInvokeDto();
		}

		if (null != dto.getBusinessCode()) {
			Map<String, Object> searchParams = new HashMap<String, Object>();
			InteractClassParamterDto interactClassParamterDto = new InteractClassParamterDto();
			interactClassParamterDto.setBusinessCode(dto.getBusinessCode());
			searchParams.put("dto", interactClassParamterDto);
			List<InteractClassParamterDto> interactClassParamterList = interactClassParamterDao.searchInteractClassParamter(searchParams);
			dto.setInteractClassParamterList(interactClassParamterList);

			searchParams = null;
		}
		return dto;

	}

	/**
	 * @description 新增执行接口信息，以及执行接口对应的参数信息
	 * @date 2015-02-02 17:47:37
	 * @param dto
	 * @return
	 * @throws
	 */
	public Long insertInteractClassInvoke(InteractClassInvokeDto dto) throws Exception {
		if (null == dto) {
			return null;
		}

		dto.setFailureCounter(Long.parseLong("0"));
		dto.setStatus(CimsConstant.INTERACT_IMPL_FALSE);// 0-未执行
		Map<String, Object> paramMap = new HashMap<String, Object>();
		paramMap.put("dto", dto);

		interactClassInvokeDao.insertInteractClassInvoke(paramMap);
		InteractClassInvokeDto resultDto = (InteractClassInvokeDto) paramMap.get("dto");

		List<InteractClassParamterDto> detailList = dto.getInteractClassParamterList();
		if (CollectionsUtils.isNotEmpty(detailList)) {
			for (InteractClassParamterDto detail : detailList) {
				paramMap = new HashMap<String, Object>();
				paramMap.put("dto", detail);
				interactClassParamterDao.insertInteractClassParamter(paramMap);
			}
		}

		paramMap = null;

		Long keyId = resultDto.getId();
		return keyId;
	}

	/**
	 * @description 更新执行接口信息
	 * @date 2015-02-02 17:47:37
	 * @param paramMap
	 * @return
	 * @throws
	 */
	private void updateInteractClassInvoke(InteractClassInvokeDto dto) throws Exception {
		Map<String, Object> paramMap = new HashMap<String, Object>();
		paramMap.put("dto", dto);

		interactClassInvokeDao.updateInteractClassInvoke(paramMap);
	}

	/**
	 * @Description 删除指定日期的若干天数以前(包含指定日期)的执行接口信息，以及接口对应的参数信息，<br>
	 *              接口参数信息无法关联到执行接口信息的数据 。 <br>
	 *              该方法可以用户清理历史垃圾数据。 <br>
	 * @param date
	 *            指定日期参数
	 * @param days
	 *            向前历史的日期数
	 * @throws Exception
	 * @author shengzhoutao
	 */
	public void deleteInvalidInteractClassInvoke(Date date, int days) throws Exception {
		// 删除指定日期的若干天数以前(包含指定日期)的执行接口信息，以及接口对应的参数信息
		this.deleteHistoryInteractClassInvoke(date, days);

		// 删除接口参数信息无法关联到执行接口信息的数据
		Map<String, Object> paramMap = new HashMap<String, Object>();
		InteractClassParamterDto dto = new InteractClassParamterDto();
		paramMap.put("dto", dto);
		interactClassParamterDao.deleteInvalidInteractClassParamter(paramMap);

		dto = null;
		paramMap = null;
	}

	/**
	 * @Description 删除指定日期的若干天数以前(包含指定日期)的执行接口信息，以及接口对应的参数信息。 <br>
	 * @param date
	 *            指定日期参数
	 * @param days
	 *            向前历史的日期数
	 * @throws Exception
	 */
	public void deleteHistoryInteractClassInvoke(Date date, int days) throws Exception {
		Date historyDate = DateTimeUtils.addDays(date, -days);

		InteractClassInvokeDto interactClassInvokeDto = new InteractClassInvokeDto();
		interactClassInvokeDto.setCreateTime(new java.sql.Timestamp(historyDate.getTime()));
		this.deleteInteractClassInvoke(interactClassInvokeDto);

		interactClassInvokeDto = null;
	}

	/**
	 * @Description 删除执行接口信息，以及接口对应的参数信息
	 * @param interactClassInvokeDto
	 *            参数条件(主键id/流水号/类名/方法名/执行状态)
	 * @throws Exception
	 *             void
	 * @author shengzhoutao
	 * @date 2015-2-3-上午10:06:37
	 * @update
	 * @throws
	 */
	public void deleteInteractClassInvoke(InteractClassInvokeDto interactClassInvokeDto) throws Exception {
		if (null == interactClassInvokeDto) {
			return;
		}

		Map<String, Object> paramMap = new HashMap<String, Object>();

		InteractClassParamterDto interactClassParamterDto = new InteractClassParamterDto();
		interactClassParamterDto.setInteractClassInvokeId(interactClassInvokeDto.getId());
		interactClassParamterDto.setInteractClassInvokeClassName(interactClassInvokeDto.getClassName());
		interactClassParamterDto.setInteractClassInvokeMethodName(interactClassInvokeDto.getMethodName());
		interactClassParamterDto.setInteractClassInvokeStatus(interactClassInvokeDto.getStatus());
		interactClassParamterDto.setBusinessCode(interactClassInvokeDto.getBusinessCode());
		interactClassParamterDto.setInteractClassInvokeCreateTime(interactClassInvokeDto.getCreateTime());
		paramMap.put("dto", interactClassParamterDto);
		interactClassParamterDao.deleteInteractClassParamter(paramMap);

		paramMap = new HashMap<String, Object>();
		paramMap.put("dto", interactClassInvokeDto);
		interactClassInvokeDao.deleteInteractClassInvoke(paramMap);

		interactClassParamterDto = null;
		paramMap = null;
	}

	/**
	 * @Description 根据主键id删除执行接口信息，以及接口对应的参数信息
	 * @param id
	 *            主键
	 * @throws Exception
	 *             void
	 * @author shengzhoutao
	 * @date 2015-2-3-上午10:06:37
	 * @update
	 * @throws
	 */
	public void deleteInteractClassInvokeById(final long id) throws Exception {
		Map<String, Object> paramMap = new HashMap<String, Object>();

		InteractClassParamterDto interactClassParamterDto = new InteractClassParamterDto();
		interactClassParamterDto.setInteractClassInvokeId(id);
		paramMap.put("dto", interactClassParamterDto);
		interactClassParamterDao.deleteInteractClassParamter(paramMap);

		paramMap = new HashMap<String, Object>();
		InteractClassInvokeDto interactClassInvokeDto = new InteractClassInvokeDto();
		interactClassInvokeDto.setId(id);
		paramMap.put("dto", interactClassInvokeDto);
		interactClassInvokeDao.deleteInteractClassInvoke(paramMap);

		interactClassInvokeDto = null;
		interactClassParamterDto = null;
		paramMap = null;
	}

	/**
	 * @Description 保存执行接口信息
	 * @param businessCode
	 *            业务编号，申请流水号，必须保证唯一性。
	 * @param executeClass
	 *            执行类
	 * @param executeMethod
	 *            执行方法名,该执行类方法要在类中定义公用<code>public</code>的方法。<br>
	 *            因为JAVA支持重载，方法的参数类型，必须与值的类型一致。<br>
	 *            传递的参数值的类型是<code>java.util.ArrayList</code> ，方法的参数类型统一当作
	 *            <code>java.util.List</code>，否则找不到该方法。<br>
	 * @param paramterObj
	 *            参数对象值,可以是多个参数的数组。如果参数值为<code>null</code>,需要<code>new</code>
	 *            一个实例对象，否则无法根据参数值获取参数类型。<br>
	 *            例如：如果参数为<code>String</code>类型,值为 <code>null</code> , 直接给空字符串;如果方法参数时间基本类型，需要参数定义对象类型，比如：int->Integer, boolean->Boolean<br>
	 *            如果参数为实体对象<code>T</code>类型,值为 <code>new T()</code>。 <br>
	 *            参数不能是数组。
	 * @return 主键id
	 * @author shengzhoutao
	 * @date 2015-2-3-下午3:08:38
	 * @update
	 * @throws Exception
	 */
	public Long saveInteractClassInvoke(final String businessCode, final Class<?> executeClass, final String executeMethod,
			final Object... paramterObjs) {
		if (SimpleStringUtils.hasText(businessCode) && SimpleStringUtils.hasText(executeMethod)) {
			InteractClassInvokeDto dto = new InteractClassInvokeDto();
			dto.setBusinessCode(businessCode);
			dto.setClassName(executeClass.getName());
			dto.setMethodName(executeMethod);

			if (null != paramterObjs) {
				List<InteractClassParamterDto> interactClassParamterList = new ArrayList<InteractClassParamterDto>();
				InteractClassParamterDto interactClassParamterDto = null;
				for (Object obj : paramterObjs) {
					if (null == obj) {
						logger.error(CimsExceptionConstant.INTERACT_CLASS_INVOKE_PARAMETER_NULL);
						throw new CimsException(CimsExceptionConstant.INTERACT_CLASS_INVOKE_PARAMETER_NULL);
					}

					String parameterName = obj.getClass().getName();
					if (ReflectUtils.isInterface(obj.getClass(), java.util.Collection.class.getName())) {
						@SuppressWarnings("rawtypes")
						java.util.Collection collection = (java.util.Collection) obj;
						parameterName = parameterName + ";" + collection.iterator().next().getClass().getName();
					}

					String parameterValue = JSON.toJSON(obj).toString();

					interactClassParamterDto = new InteractClassParamterDto();
					interactClassParamterDto.setBusinessCode(businessCode);
					interactClassParamterDto.setParameterName(parameterName);
					interactClassParamterDto.setParameterValue(parameterValue);

					interactClassParamterList.add(interactClassParamterDto);
				}
				dto.setInteractClassParamterList(interactClassParamterList);

				interactClassParamterDto = null;
				interactClassParamterList = null;
			}

			try {
				return this.insertInteractClassInvoke(dto);

			} catch (Exception e) {
				logger.error(e.getMessage());
				throw new CimsException(e.getMessage());
			}
		}

		return null;
	}

	/**
	 * @Description 根据主键ID执行接口信息
	 * @param id
	 *            主键ID
	 * @return 执行接口返回结果。如果接口没有返回值，返回将是空；如果接口有返回非空值，返回有值，需要此值需要值做转换。
	 * @throws Exception
	 * @author shengzhoutao
	 * @date 2015-2-3-下午4:12:43
	 * @update
	 * @throws
	 */
	public Object exeInteractClassInvokeById(final String id) throws Exception {
		InteractClassInvokeDto interactClassInvokeDto = queryInteractClassInvokeByPrimaryKey(id);

		if (null == interactClassInvokeDto) {
			throw new CimsException("id[" + id + "]的接口信息不存在.");
		}

		return this.invoke(interactClassInvokeDto);
	}

	/**
	 * @Description 根据业务流水号执行接口信息
	 * @param businessCode
	 *            业务流水号
	 * @return 执行接口返回结果。如果接口没有返回值，返回将是空；如果接口有返回非空值，返回有值，需要此值需要值做转换。
	 * @throws Exception
	 * @author shengzhoutao
	 * @date 2015-2-3-下午8:45:34
	 * @update
	 * @throws
	 */
	public Object exeInteractClassInvokeByBusinessCode(final String businessCode) throws Exception {
		InteractClassInvokeDto interactClassInvokeDto = interactClassInvokeDao.findInteractClassInvokeByBusinessCode(businessCode);

		if (null == interactClassInvokeDto) {
			throw new CimsException("业务流水号[" + businessCode + "]的接口信息不存在.");
		}

		return this.invoke(interactClassInvokeDto);
	}

	/**
	 * @Description 执行接口信息
	 * @param interactClassInvokeDto
	 * @return 执行接口返回结果。如果接口没有返回值，返回将是空；如果接口有返回非空值，返回有值，需要此值需要值做转换。
	 * @throws Exception
	 *             void
	 * @author shengzhoutao
	 * @date 2015-2-4-下午2:29:43
	 * @update
	 * @throws
	 */
	private Object invoke(InteractClassInvokeDto interactClassInvokeDto) throws Exception {
		if (null == interactClassInvokeDto) {
			return null;
		}
//取消执行次数限制
//		if (CimsConstant.INTERACT_IMPL_TRUE.equals(interactClassInvokeDto.getStatus())) {
//			throw new CimsException(MessageFormat.format(CimsExceptionConstant.INTERACT_CLASS_INVOKE_ALREADY_EXECUTE, new Object[] {
//					interactClassInvokeDto.getId().toString(), interactClassInvokeDto.getBusinessCode() }));
//		}

		if (interactClassInvokeDto.getFailureCounter() > CimsConstant.MAX_FAILURE_COUNTER) {
			throw new CimsException(MessageFormat.format(CimsExceptionConstant.INTERACT_CLASS_INVOKE_EXCEED_MAX_FAILURE_COUNTER, new Object[] {
					interactClassInvokeDto.getId().toString(), interactClassInvokeDto.getBusinessCode(), CimsConstant.MAX_FAILURE_COUNTER }));
		}

		Object result = null;

		// 修改InteractClassInvoke的状态:0-未执行
		this.updateInteractClassInvokeStatusById(interactClassInvokeDto.getId(), CimsConstant.INTERACT_IMPL_FALSE);// 0-未执行

		try {
			// 解析执行接口信息,通过反射调用方法.
			result = this.doInvoke(interactClassInvokeDto);
		} catch (InvocationTargetException e) {
			// 根据id修改InteractClassInvoke的失败次数
			updateInteractClassInvokeFailureCounterById(interactClassInvokeDto.getId(), interactClassInvokeDto.getFailureCounter() + 1);
						
		    Throwable throwable = e.getTargetException();// 获取目标异常  
			throw new CimsException(throwable.getMessage());
		} catch (Exception e) {
			// 根据id修改InteractClassInvoke的失败次数
			updateInteractClassInvokeFailureCounterById(interactClassInvokeDto.getId(), interactClassInvokeDto.getFailureCounter() + 1);
			throw new CimsException("解析执行接口信息,通过反射调用方法异常:", e);
		}

		// 修改InteractClassInvoke的状态: 1-已执行
		this.updateInteractClassInvokeStatusById(interactClassInvokeDto.getId(), CimsConstant.INTERACT_IMPL_TRUE);// 1-已执行

		return result;
	}

	/**
	 * @Description 解析执行接口信息,通过反射调用方法.
	 * @param interactClassInvokeDto
	 * @return 执行接口返回结果。如果接口没有返回值，返回将是空；如果接口有返回非空值，返回有值，需要此值需要值做转换。
	 * @throws Exception
	 *             void
	 * @author shengzhoutao
	 * @date 2015-2-4-下午2:29:48
	 * @update
	 * @throws
	 */
	public Object doInvoke(InteractClassInvokeDto interactClassInvokeDto) throws Exception {
		if (null == interactClassInvokeDto) {
			return null;
		}

		String className = interactClassInvokeDto.getClassName();
		String methodName = interactClassInvokeDto.getMethodName();
		List<InteractClassParamterDto> interactClassParamterList = interactClassInvokeDto.getInteractClassParamterList();

		Class<?>[] parameterTypesTure = null;
		Object[] args = null;

		// 转化参数的类，json转化为java对应类型值
		if (CollectionsUtils.isNotEmpty(interactClassParamterList)) {
			int paramterSize = interactClassParamterList.size();
			parameterTypesTure = new Class[paramterSize];
			args = new Object[paramterSize];

			for (int index = 0; index < paramterSize; index++) {
				InteractClassParamterDto interactClassParamterDto = interactClassParamterList.get(index);
				String[] parameterName = interactClassParamterDto.getParameterName().split("<");
				String parameterValue = interactClassParamterDto.getParameterValue();
				Class<?> parameterClass = Class.forName(parameterName[0]);

				if (parameterName[0].contains(java.util.ArrayList.class.getName())) {
					parameterTypesTure[index] = java.util.List.class;
				} else {
					parameterTypesTure[index] = parameterClass;
				}

				if (String.class.getName().equals(parameterName[0])) {
					args[index] = parameterValue;
				} else {
					if (ReflectUtils.isInterface(parameterClass, java.util.Collection.class.getName())) {
						if (parameterName.length > 1) {
							String clazzStr = parameterName[1];
							parameterClass = Class.forName(clazzStr.substring(0, clazzStr.length() - 1));
							args[index] = JSONArray.parseArray(parameterValue, parameterClass);
						} else {
							args[index] = JSONArray.parseArray(parameterValue, Object.class);
						}
					} else {
						args[index] = JSON.parseObject(parameterValue, parameterClass);
					}
				}
			}
		}

		Method method = Class.forName(className).getMethod(methodName, parameterTypesTure);// 注意：执行方法需要定义公用方法

		if (null == method) {
			throw new CimsException(MessageFormat.format(CimsExceptionConstant.INTERACT_CLASS_INVOKE_INCORRECT_PARAMETER, new Object[] {
					interactClassInvokeDto.getId(), interactClassInvokeDto.getBusinessCode() }));
		}

		Object bean = springContextUtils.getBean(className);
		if (null == bean) {
			throw new CimsException(MessageFormat.format(CimsExceptionConstant.NOT_INTO_BEAN, new Object[] { className }));
		}
		// 通过反射获取要调用的方法
		// ReflectUtils.invokeMethod(bean, methodName, parameterTypes, args);
		Object result = method.invoke(bean, args);

		args = null;

		return result;
	}

	/**
	 * @Description 根据id修改InteractClassInvoke的状态
	 * @param 主键id
	 * @param status
	 *            状态
	 * @throws Exception
	 * @author shengzhoutao
	 */
	public void updateInteractClassInvokeStatusById(final long id, final String status) throws Exception {
		InteractClassInvokeDto interactClassInvoke = new InteractClassInvokeDto();
		interactClassInvoke.setId(id);
		interactClassInvoke.setStatus(status);

		this.updateInteractClassInvoke(interactClassInvoke);

		interactClassInvoke = null;
	}

	/**
	 * @Description 根据id修改InteractClassInvoke的失败次数
	 * @param 主键id
	 * @param failureCounter
	 *            失败次数
	 * @throws Exception
	 * @author shengzhoutao
	 */
	private void updateInteractClassInvokeFailureCounterById(final long id, final long failureCounter) throws Exception {
		InteractClassInvokeDto interactClassInvoke = new InteractClassInvokeDto();
		interactClassInvoke.setId(id);
		interactClassInvoke.setFailureCounter(failureCounter);
		interactClassInvoke.setStatus(CimsConstant.INTERACT_IMPL_FALSE);
		this.updateInteractClassInvoke(interactClassInvoke);

		interactClassInvoke = null;
	}
}
